﻿using System;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Printing;
using Word = Microsoft.Office.Interop.Word;
using Excel = Microsoft.Office.Interop.Excel;
using System.Threading;
using System.Collections.Generic;

namespace BatchPrintHelper
{
    public partial class MainForm : Form
    {
        private bool isBusy = false;
        private bool needToExit = false;
        private FolderBrowserDialog selectPathDlg;
        public string SelectedPath
        {
            get { return txtPath.Text; }
            set
            {
                txtPath.Text = value;
                listFiles.Items.Clear();
                if (!String.IsNullOrWhiteSpace(SelectedPath))
                {
                    foreach (var file in Directory.EnumerateFiles(SelectedPath))
                    {
                        if ( Path.GetFileName(file).StartsWith("~$"))
                        {
                            continue;
                        }
                        if (Helper.IsWordDoc(file))
                        {
                            if(wordApp != null)
                            {
                                listFiles.Items.Add(file);
                            }
                        }
                        else if(Helper.IsExcelDoc(file) && excelApp != null)
                        {
                            listFiles.Items.Add(file);
                        }

                    }
                }
            }
        }

        public string SelectedPrinter
        {
            get { return cbPrinterList.SelectedItem as string; }
            set { cbPrinterList.SelectedItem = value; }
        }

        private Word.Application wordApp;

        private Excel.Application excelApp;


        public MainForm()
        {
            InitializeComponent();
            this.Text = $"{Const.APP_NAME}  ver .{Application.ProductVersion}";
            this.btnSelectDir.Click += BtnSelectDir_Click;
            this.btnExit.Click += BtnExit_Click;
            this.btnPrint.Click += BtnPrint_Click;
            this.btnPrint.Enabled = false;
            this.Load += MainForm_Load;
            this.txtPath.ReadOnly = true;
            this.cbPrinterList.DropDownStyle = ComboBoxStyle.DropDownList;
            this.FormClosing += MainForm_FormClosing;
            this.cbFilterSheet.CheckedChanged += CbFilterSheet_CheckedChanged;
        }

        private void CbFilterSheet_CheckedChanged(object sender, EventArgs e)
        {

                txtSheetNameWhiteList.Enabled = cbFilterSheet.Checked; 
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.isBusy)
            {
                DialogResult dr = MessageBox.Show(this, "正在打印中,是否确定退出?", 
                    Const.APP_NAME, MessageBoxButtons.YesNo);

                needToExit = dr == DialogResult.Yes;
                e.Cancel = true;
            }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            foreach (string printerName in PrinterSettings.InstalledPrinters)
            {
                cbPrinterList.Items.Add(printerName);
            }

            try
            {
                SelectedPrinter =new PrinterSettings().PrinterName;
            }
            catch (Exception ex)
            {
                var message = "Error Occured while initlize the default printer!,"
                    + $"Please checked if printer {SelectedPrinter} is ready!";
                Helper.MessageBoxException(ex, message);
            }

            CreateApps();

        }

        private void CreateApps()
        {
            wordApp = CreateWordApp();
            excelApp = CreateExcelApp();
            if (wordApp == null && excelApp == null)
            {
                MessageBox.Show("We can't work without Word and Excel!");
                btnPrint.Enabled = false;
            }
            else if (wordApp == null)
            {
                
                cbPrinterList.Visible = false;
                Helper.MessageBoxShow("Only Excel documents can be printed!");
            }
            else if (excelApp == null)
            {
                Helper.MessageBoxShow("Only Word docments can be printed!");
            }
        }

        private void BtnPrint_Click(object sender, EventArgs e)
        {

            if (cbFilterSheet.Checked)
            {
                if(String.IsNullOrWhiteSpace(txtSheetNameWhiteList.Text))
                {
                    Helper.MessageBoxShow("Please input the sheet names you want to print, with ';' as split char.");
                    return;
                }
            }
            try
            {
                isBusy = true;
                UpdateUI(false);
                bool skipAllExceptions = false;

                while (!needToExit && listFiles.Items.Count > 0)
                {
                    var file = listFiles.Items[0] as string;
                    try
                    {
                        Print(file,cbToFiles.Checked);
                        listFiles.Items.RemoveAt(0);
                    }
                    catch (Exception ex)
                    {
                        var dr = DialogResult.Ignore;
                        if (!skipAllExceptions)
                        {
                            dr = AskUserForAction(file, ex);
                        }
                        const DialogResult DR_SkipAllExceptions = DialogResult.No;
                        if (dr == DialogResult.Ignore)
                        {
                            listFiles.Items.RemoveAt(0);
                        }
                        else if (dr == DialogResult.Abort)
                        {
                            break;
                        }
                        else if (dr == DR_SkipAllExceptions)
                        {
                            listFiles.Items.RemoveAt(0);
                            skipAllExceptions = true;
                        }
                    }

                }

            }
            finally
            {

                UpdateUI(true);
                isBusy = false;
                if (needToExit)
                {
                    Close();
                }
            }
        }

        private void UpdateUI(bool enabled)
        {
            if (enabled)
            {
                SetBtnPrintEnabled();
            }
            else
            {
                btnPrint.Enabled = false;
            }
            btnSelectDir.Enabled = enabled;
            btnExit.Enabled = enabled;
            cbPrinterList.Enabled = enabled;
        }

        private DialogResult AskUserForAction(string file, Exception ex)
        {
            DialogResult dr;
            var errorInfo = $@"Current processing file:
{file}
Exception->Message:
{ex.Message}
Exception->StackTrack:
{ex.StackTrace}";
            using (var dlg = new ConfirmErrorActionDialog() { ErrorInfo = errorInfo })
            {
                dr = dlg.ShowDialog(this);
            }

            return dr;
        }

        private static void GetAction(ref bool skipAllExceptions,  ref bool bContinue,
            System.Collections.IEnumerator enumerator, DialogResult dr)
        {
            const DialogResult DR_SkipAll = DialogResult.No;
            switch (dr)
            {
                case DialogResult.Ignore:
                    bContinue = enumerator.MoveNext();
                    break;
                case DialogResult.Abort:
                    bContinue = false;
                    break;
                case DR_SkipAll:
                    bContinue = enumerator.MoveNext();
                    skipAllExceptions = true;
                    break;
                case DialogResult.Retry:
                default:
                    break;
            }
        }

        private void Print(string file,bool isToFile)
        {
            // Using OpenOld so as to be compatible with other versions of Word

;
            if (Helper.IsExcelDoc(file))
            {

                var workbook = excelApp.Workbooks.Open(file);

                //Access Violation will occur 
                //if we set ActivePrinter before openning the first workbook.
                //so ,we set printer after a workbook opened;
                
                foreach(var printerName in Helper.ConvertToExcelPrinterFriendlyName(SelectedPrinter))
                {
                    try
                    {
                        excelApp.ActivePrinter = printerName;
                        break;
                    }
                    catch
                    {

                    }
                }

                excelApp.DisplayAlerts = false;
                try
                {
                   
                    foreach (Excel.Worksheet sheet in workbook.Worksheets)
                    {
                        if (!cbFilterSheet.Checked)
                        {
                            if (sheet.Visible != Excel.XlSheetVisibility.xlSheetVisible)
                            {
                                continue;
                            }
                        }else  if (!txtSheetNameWhiteList.Text.Contains(sheet.Name))
                        {
                            continue;
                        }
                        sheet.PageSetup.PrintComments =
                            cbPrintCommets.Checked
                            ? Excel.XlPrintLocation.xlPrintInPlace
                            : Excel.XlPrintLocation.xlPrintNoComments;
                      
                    }

                    Excel.Sheets toPrintSheets = null;
                    string[] sheetNames = null;
                    if (cbFilterSheet.Checked)
                    {
                        string[] temp = txtSheetNameWhiteList.Text.Split(';');
                        List<string> validSheetNames = new List<string>();
                        foreach(string sheetName in temp)
                        {
                            foreach(Excel.Worksheet ws in workbook.Worksheets)
                            {
                                if(ws.Name == sheetName)
                                {
                                    validSheetNames.Add(sheetName);
                                    break;
                                }
                            }
                        }

                        sheetNames = validSheetNames.ToArray();
                        

                    }
                    else
                    {
                        List<string> sheetNamesList = new List<string>();
                        foreach (Excel.Worksheet sheet in workbook.Worksheets)
                        {
                            if (sheet.Visible == Excel.XlSheetVisibility.xlSheetVisible)
                            {
                                sheetNamesList.Add(sheet.Name);
                            }
                        }
                        sheetNames = sheetNamesList.ToArray();
                        
                    }

                    if(sheetNames != null && sheetNames.Length > 0)
                    {
                        toPrintSheets = ((Excel.Sheets)workbook.Worksheets.get_Item(sheetNames));
                        if (isToFile)
                        {
                            string fileName =
                                Path.Combine(Path.GetDirectoryName(file),
                                Path.GetFileNameWithoutExtension(file) + ".pdf" );
                            toPrintSheets.PrintOutEx(PrintToFile: true, PrToFileName: fileName);

                        }
                        else
                        {
                            toPrintSheets.PrintOutEx();
                        }
                    }

 
                }
                finally
                {
                    workbook.Close(SaveChanges:false);
                }
            }
            else
            {
                var document = wordApp.Documents.OpenOld(file);
                wordApp.ActivePrinter = SelectedPrinter;
                try
                {
                    while (wordApp.BackgroundPrintingStatus > 0)
                    {
                        Thread.Sleep(200);
                    }
                    if (isToFile)
                    {
                        string fileName =
                            Path.Combine(Path.GetDirectoryName(file),
                            Path.GetFileNameWithoutExtension(file) + ".pdf");

                        document.PrintOut(PrintToFile: true,OutputFileName: fileName);
                    }
                    else
                    {

                        document.PrintOut();
                    }
                }
                finally
                {
                    document.Close(SaveChanges: false);
                    //wordApp.Documents.Close(SaveChanges:false);
                }

            }
        }

        private static Word.Application CreateWordApp()
        {
            Word.Application app = null;
            try
            {
                app = new Word.Application();
                app.Visible = false;
                app.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
            }
            catch (Exception ex)
            {
                var message = "An error occured while initializing Word," 
                    + "Please check if Office was properly installed on this computer!";
                Helper.MessageBoxException(ex, message);
            }

            //app.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

            return app;
        }
        private static Excel.Application CreateExcelApp()
        {
            Excel.Application app = null;
            try
            {
                app = new Excel.Application();
                app.Visible = false;
            }
            catch (Exception ex)
            {
                var message = "An error occured while initializing Excel,"
                    + "Please check if Office was properly installed on this computer!";
                Helper.MessageBoxException(ex, message);
            }

          
            return app;
        }


        private void BtnSelectDir_Click(object sender, EventArgs e)
        {
            if(selectPathDlg == null)
            {
                selectPathDlg = new FolderBrowserDialog();
            }

            if (selectPathDlg.ShowDialog() == DialogResult.OK)
            {
                SelectedPath = selectPathDlg.SelectedPath;
                SetBtnPrintEnabled();
            }
        }

        private void SetBtnPrintEnabled()
        {
            btnPrint.Enabled = listFiles.Items.Count > 0
                && cbPrinterList.SelectedItem != null;
        }

        private void BtnExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
